package com.code.leetcode._202508;

import java.util.Arrays;

//62 不同路径
public class UniquePaths {
    /**
     * 一个机器人位于一个m*n网格的左上角(起始点在下图中标记为"Start")
     * 机器人每次只能向下或者向右移动一步，机器人试图达到网格的右下角(在下图中标记为"Finish")
     * 问总共有多少条不同的路径？
     * 示例1：输入：m = 3, n = 7
     * 输出：28
     * 示例2：输入：m = 3, n = 2
     * 输出：3
     * 解释：
     * 从左上角开始，总共有 3 条路径可以到达右下角。
     * 1. 向右 -> 向下 -> 向下
     * 2. 向下 -> 向下 -> 向右
     * 3. 向下 -> 向右 -> 向下
     * 示例3：输入：m = 7, n = 3
     * 输出：28
     * 示例4：输入：m = 3, n = 3
     * 输出：6
     **/
    public static void main(String[] args) {
        UniquePaths u = new UniquePaths();
        System.out.println(u.uniquePaths2(3,7));
    }

    /**
     * 动态规划
     * 我们用f(i,j)表示从左上角走到(i,j)的路径数量，其中i和j的范围分别是[0,m]和[0,n)
     * 由于我们每一步只能从向下或者向右移动一步，因此要想走到(i,j)，如果向下走一步，那么会(i-1,j)走过来，
     * 如果向右走一步，那么会从f(i,j-1)走过来。因此我们可以写处动态规划转移方程：f(i,j)=f(i-1,j)+f(i,j-1)
     * 需要注意的是，如果i=0，那么f(i-1,j)并不是一个满足要求的状态，我们需要忽略这一项；同理如果j=0,那f(i,j-1)
     * 并不是一个满足要求的状态，我们需要忽略这一项。
     * 初始条件为f(0,0)=1，即从左上角走到左上角有一种方法，最终的答案即为f(m-1,n-1)
     **/
    public int uniquePaths(int m, int n) {
        int[][] f = new int[m][n];
        for (int i = 0; i < m; i++) {
            f[i][0] = 1;
        }
        for (int j = 0; j < n; j++) {
            f[0][j] = 1;
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                f[i][j] = f[i - 1][j] + f[i][j - 1];
            }
        }
        return f[m - 1][n - 1];
    }

    /**
     * 此外，由于f(i,j)仅与第i行和第i-1行的状态有关，因此我们可以使用滚动数组代替代码中的二维数组，使空间复杂度降低为O(n)
     **/
    public int uniquePaths1(int m, int n) {
        int[] f = new int[n];
        Arrays.fill(f, 1);
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                f[j] += f[j - 1];
            }
        }
        return f[n - 1];
    }

    /**
     * 组合数学
     * 从左上角到右下角的过程中，我们需要移动m+n-2次，其中有m-1次向下移动，n-1次向右移动。
     * 因此路径的总数，就等于从m+n-2次移动中选择m-1次向下移动的方案数，即组合数：
     * (m+n-2)!/((m-1)!(n-1)!),因此我们直接计算出这个组合数即可。计算的方法有很多种：
     * 1、如果使用的语言有组合数计算的API，我们可以调用API计算
     * 2、如股票没有相应的API。我们可以使用(m+n-2)(m+n-3)...n/(m-1)!进行计算
     **/
    public int uniquePaths2(int m, int n) {
        long ans = 1;
        for (int x = n, y = 1; y < m; ++x, ++y) {
            ans = ans * x / y;
        }
        return (int) ans;
    }
}
